package org.witsoft.dtx.example.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.witsoft.dtx.example.domain.WalletEvent;
import org.witsoft.dtx.example.domain.WalletLog;
import org.witsoft.dtx.example.service.WalletService;

import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;

/**
 * @author XiaoQing Lin
 * @date 2020-09-21 16:02
 */
@Service
public class WalletServiceImpl implements WalletService {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void increase(int userId, BigDecimal money, WalletEvent event, String orderId , boolean unusable, String description) {
        money = money.abs();
        if(unusable) {
            jdbcTemplate.update("update wallet set unusable_amount=unusable_amount+? where user_id=?", money, userId);
        }else{
            jdbcTemplate.update("update wallet set usable_amount=usable_amount+? where user_id=?", money, userId);
        }
        jdbcTemplate.update("insert into wallet_log(user_id, change_amount, event, order_id, create_time, description, unusable)" +
                        " values(?,?,?,?,?,?,?)",
                userId, money, event.getValue(), orderId, new Date(), description, unusable
                );
    }


    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public boolean decrease(int userId, BigDecimal money, WalletEvent event , String orderId, boolean unusable, String description) {
        money = BigDecimal.ZERO.subtract(money.abs());
        int effects = 0;
        if(unusable) {
            effects = jdbcTemplate.update("update wallet set unusable_amount=unusable_amount+? where user_id=? and unusable_amount>=?", money, userId, money);
        }else{
            effects = jdbcTemplate.update("update wallet set usable_amount=usable_amount+? where user_id=? and usable_amount>=?", money, userId, money);
        }
        if(effects > 0){
            jdbcTemplate.update("insert into wallet_log(user_id, change_amount, event, order_id, create_time, description,unusable)" +
                            " values(?,?,?,?,?,?,?)",
                    userId, money, event.getValue(), orderId, new Date(), description, unusable
            );
        }
        return effects > 0;
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void transferToUsable(int userId, WalletEvent event, String orderId) {
        WalletLog walletLog = findLog(userId, event, orderId);
        if(walletLog == null || !walletLog.getUnusable()){
            return;
        }
        BigDecimal changeAmount = walletLog.getChangeAmount();
        int effects = jdbcTemplate.update("update wallet_log set unusable=0 where id=? and unusable=1", walletLog.getId());
        if(effects > 0) {
            jdbcTemplate.update("update wallet set usable_amount=usable_amount+?,unusable_amount=unusable_amount-?" +
                    " where user_id=? and unusable_amount>=?", changeAmount, changeAmount, userId, changeAmount);
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void revert(int userId, WalletEvent event, String orderId) {
        WalletLog walletLog = findLog(userId, event, orderId);
        if(walletLog == null){
            return;
        }
        int effects = jdbcTemplate.update("update wallet_log set reverted=1 where id=? and reverted=0", walletLog.getId());
        if(effects == 0){
            return;
        }
        BigDecimal changeAmount = walletLog.getChangeAmount().abs();
        boolean unusable = walletLog.getUnusable();
        if(walletLog.getChangeAmount().compareTo(BigDecimal.ZERO) > 0){
            this.decrease(userId, changeAmount, WalletEvent.RETURN_OUT, orderId, unusable, "交易中断退还");
        }else{
            this.increase(userId, changeAmount, WalletEvent.RETURN_IN, orderId, unusable, "交易中断退回");
        }
    }

    public WalletLog findLog(int userId, WalletEvent event, String orderId) {
        String sql = "SELECT * FROM wallet_log WHERE user_id = ? and event = ? and order_id = ?";
        List<WalletLog> list = jdbcTemplate.query(sql, new RowMapper<WalletLog>() {
            @Override
            public WalletLog mapRow(ResultSet rs, int i) throws SQLException {
                BigDecimal changeAmount = rs.getBigDecimal("change_amount");
                WalletLog entity = new WalletLog();
                entity.setId(rs.getLong("id"));
                entity.setUnusable(rs.getBoolean("unusable"));
                entity.setOrderId(rs.getString("order_id"));
                entity.setChangeAmount(changeAmount);
                return entity;
            }
        }, userId, event.getValue(), orderId);
        if(list.isEmpty()){
            return null;
        }
        return list.get(0);
    }
}
